/*
 * LServer.cpp
 *
 *  Created on: Dec 4, 2010
 *      Author: lycdragon
 */

#include "LServer.h"
#include <set>
struct parm{
	int sd;
	struct  sockaddr_in cad;
};
struct buffer {
		char buff[512];
	};

map < string, bool > lsmap;

int sockfd;
int sockfdtoclient;
int sockfdtolocalserver;

char csipaddr[20];
char sharefolder[50];
int csport;
int portforclient;
int portforls;
int chunkInfoUpdateInterval=15;

int update_chunk_info(int sockfd);
void* lsStartsClientThread(void *pParam);
void* sendChunkInfoPeriodically(void* proc);
void sendfile_New(int sd2, string name);
int saveFileFromServer(string file,string ipLS,int portLS);

/**
 * New implementation
 */
LServer::LServer(){

	cout<<"Loading configuration file LSconfig.ini..."<<endl;
	ifstream fin("LSconfig.ini");
	fin >> csipaddr >> csport;
	fin >> portforclient >> portforls;
	fin >> sharefolder;
	cout<<"CS IP :"<<csipaddr<<", CS Port:"<<csport<<endl;
	cout<<"My Port :"<<portforclient<<endl;
	cout<<"Shared Folder :"<<sharefolder<<endl;
	string name("Local Server");
	this->name=name;


	//lunch a tread to periodically update chunk information
		pthread_t updateThread;
		int thrdRef=pthread_create(&updateThread,NULL,sendChunkInfoPeriodically,(void*)NULL);
	    //int thrdRef=pthread_create(&updateThread, NULL, sendChunkInfoPeriodically,(void *)NULL);
	    if(thrdRef != 0)
		{
			cerr << "Fail to create thread: local server" << endl;
		}


	struct  sockaddr_in cad;
	size_t  alen;
	int sd2;
	int sd = asServer(name, portforclient);
	bool firstTime=true;

    while (1)
    {
    	/* Specify size of request queue */
    	if (listen(sd, QLEN) < 0)
    	{
   	        cerr << "listen failed" << endl;
   	        exit(0);
   	    }
		cout.flush();

		if(firstTime){
        	cout << name << " is waiting for a client connection in PORT:"<<portforclient << endl;
        	firstTime=false;
        }


		alen = sizeof(cad);
        if ( (sd2 = accept(sd, (struct sockaddr *)&cad, &alen)) < 0)
    	{
            cerr << name << ":"<< "accept failed" << endl;
        }


  		cout << name << " received and accepted a client connection from :" << inet_ntoa(cad.sin_addr) << endl;

        if(send(sd2,"Connect success!", 17 ,0) < 0)
        {
        	cerr << name << ":" << "Send connection information failed" << endl;
        	continue;
        }

        struct parm temp;
        temp.sd = sd2;
        temp.cad = cad;

        pthread_attr_t ma;
        pthread_attr_init(&ma);
        pthread_attr_setdetachstate(&ma, PTHREAD_CREATE_JOINABLE);
        pthread_t pt;

        if(pthread_create(&pt, &ma, lsStartsClientThread,(void*)(&temp)) != 0)
        {
        	cerr << "Fail to create thread for client" << sd2;
        }

        pthread_attr_destroy(&ma);
    }

}

void* sendChunkInfoPeriodically(void* proc){

	while(true){
		//create a socket to the central server
		int sockfd = asClient("Local Server", csipaddr, csport);
		update_chunk_info(sockfd);
		sleep(chunkInfoUpdateInterval);
	}
}

void donateChunk(Message msg, int clientSocket){
	string file=msg.getMessage();
    string fname = "LSsharedFolder/"+file;
    char buf[1000];
    strcpy(buf,fname.c_str());
    sendfile_New(clientSocket,buf);
}

void sendfile_New(int sd2, string name) {
	struct stat sbuf;
	int i;


	struct buffer mainbuffer;

	int inputfd, bytesread, byteswritten;
	int statsuccess;
	int bufferno;
	float bufferf;
	char buf[1000];
	strcpy(buf, name.c_str());

	/* Try to open the inputfilename, if the file does not exist, abort*/
	inputfd = open((char *) buf, O_RDONLY, 0666);
	if (inputfd < 0) {
		printf(" The input file could not be opened\n");
		return;
	}
	statsuccess = stat((char *) buf, &sbuf);
	if (statsuccess != 0) {
		printf(" There is no file with the specified name\n");
		exit(1);
	}
	/* Calculate the number of times read and write has to be done for the whole file, each time sizeof mainbuffer.buf data is copied */
	bufferno = sbuf.st_size / sizeof(mainbuffer.buff);
	bufferf = (float) sbuf.st_size / sizeof(mainbuffer.buff);
	i = 0;
	/*Copy the content from sourcefile to destinationfile*/
	if (sbuf.st_size <= sizeof(mainbuffer.buff)) {
		bytesread = read(inputfd, mainbuffer.buff, sbuf.st_size);
		byteswritten = write(sd2, mainbuffer.buff, sbuf.st_size);
		close(sd2);
	} else {
		for (i = 0; i < bufferno; i++) {
			bytesread = read(inputfd, mainbuffer.buff, sizeof(mainbuffer.buff));
			byteswritten = write(sd2, mainbuffer.buff, sizeof(mainbuffer.buff));
		}

		if (bufferf > (float) bufferno) {
			bytesread = read(inputfd, mainbuffer.buff, (sbuf.st_size
					- (sizeof(mainbuffer.buff) * i)));
			byteswritten = write(sd2, mainbuffer.buff, (sbuf.st_size
					- (sizeof(mainbuffer.buff) * i)));
			close(sd2);
		}
	}
	close(inputfd);
}


void serveClientSearchRequest(Message msg, int clientSocket)
{
	int socketToCS;
	int numbytes;
    string fname = "LSsharedFolder/";
    string query = msg.getMessage();
    cout << "Requested File Name:" << query << endl;
    socketToCS = asClient("LocalServer", csipaddr, csport);
    MessageLS2CSReq ls2csReq(LS_2_CS_REQ,query);
    string req = ls2csReq.encode();
    char buf[1000];
    strcpy(buf, req.c_str());
    send(socketToCS, buf, req.length(), 0);
    if((numbytes = recv(socketToCS, buf, 1000, 0)) == -1)
        cerr << "doClient: " << "Receive message fail" << endl;
    close(socketToCS);

    buf[numbytes] = '\0';
    string csReply;
    csReply.assign(buf);
    MessageChunkInfo ls2csChunkInfo1=MessageChunkInfo(CS_2_LS_REP);
    ls2csChunkInfo1.setChunksFromRawMsg(csReply);
    cout << "Response from Central Server:" << endl;
    std::set<string> chunksToClient;
    std::vector<string> chunks = ls2csChunkInfo1.getChunks();
    for(vector<string>::iterator it = chunks.begin();it != chunks.end();++it){
        string ipPortChunk = *it;
        cout << ipPortChunk << endl;
		int index=ipPortChunk.find(":");
		string portChunk=ipPortChunk.substr(index+1);
		string ipLS=ipPortChunk.substr(0,index);
		index=portChunk.find(":");
		string chunk=portChunk.substr(index+1);
		int portLS=atoi(portChunk.substr(0,index).c_str());
		if(portforclient!=portLS){ //if ip is mine, no need to download
			cout<<"Trying to connect:::"<<ipLS<<" "<<" "<<portLS<<" "<<chunk<<endl;
			saveFileFromServer(chunk,ipLS,portLS);
		}
		chunksToClient.insert(chunk);
	}
    std::vector<string> opToClients;
    for(set<string>::iterator it = chunksToClient.begin();it != chunksToClient.end();++it){
    	string chunkName=*it;
    	opToClients.push_back(chunkName);
    }

    cout << "Sending gathered chunk list to client" << endl;
    char buf1[MAXDATASIZE];
    MessageChunkInfo chunkInfoToClient = MessageChunkInfo(opToClients, MSG_CHUNK_INFO_ADV);
    string encodedString = chunkInfoToClient.encode();
    strcpy(buf1, encodedString.c_str());
    send(clientSocket, buf1, encodedString.length(), 0);
    close(clientSocket);
}

void* lsStartsClientThread(void *pParam)
{
	char buf[MAXDATASIZE];
	struct parm *p = (struct parm*)(pParam);
	char ipaddr[20];
	int numbytes;
	string encodedString;
	Message msg;
	bool success;
	int clientSocket = p -> sd;
	strcpy(ipaddr, inet_ntoa(p -> cad.sin_addr));

	if ((numbytes = recv(clientSocket, buf,  MAXDATASIZE, 0)) == -1){
		cerr << "Receive message fail" << endl;
	}
	encodedString.assign(buf);
	success = msg.decode(encodedString);
	if (success) {
		if (msg.getMessageType() == CLIENT_ST_NAME) {
			serveClientSearchRequest(msg, clientSocket);
		}
		else if( msg.getMessageType()== LS_2_LS_GET_CHUNK ){
			cout<<"Received LS2LSGetChunk:"<<encodedString<<endl;
			donateChunk(msg, clientSocket);
		}
		else if(msg.getMessageType() == CL_2_LS_GET_CHUNK){
			donateChunk(msg, clientSocket);
		}

}
}


int update_chunk_info(int sockfd)
{
	MessageChunkInfo ls2csChunkInfo(MSG_CHUNK_INFO_ADV);
	vector <string> v;
	v.clear();

	DIR *dp;
	struct dirent *dirp;
	if((dp = opendir(sharefolder)) == NULL)
		cout << "Cannot open dir" << sharefolder << endl;
	string fna;
	while((dirp = readdir(dp)) != NULL){
		string path;

		if(dirp -> d_name[0] == '.') continue;
		path.assign(dirp -> d_name);
		stringstream ss;
		ss << portforclient;
		path=ss.str()+":"+path;
		v.push_back(path);
		fna.assign(dirp -> d_name);
		lsmap[fna] = 1;
	}

	char buf[MAXDATASIZE];

	ls2csChunkInfo = MessageChunkInfo(v, MSG_CHUNK_INFO_ADV);
	string encodedString = ls2csChunkInfo.encode();

	strcpy(buf, encodedString.c_str());

	send(sockfd, buf, encodedString.length(), 0);
	close(sockfd);
	return 0;
}
int saveFileFromServer(string file,string ipLS,int portLS){
	char fileName[100];
	char serverIP[100];
	strcpy(serverIP,ipLS.c_str());

	int opfd;
	struct buffer aBuffer;
	int n;
	string query=file;
	Message msg;
	msg.setMessageType(LS_2_LS_GET_CHUNK);
	msg.setMessage(query);

	int sockfd = asClient("LocalServer",serverIP,portLS);
	char buf[MAXDATASIZE];
	strcpy(buf,msg.encode().c_str());
	send(sockfd, buf, msg.encode().length(), 0);

	string localFileName;
	localFileName.assign(sharefolder);
	localFileName+="/"+query;

	cout<<"Trying to get "<<query<<" "<<" and store in "<<localFileName<<endl;

	strcpy(fileName, localFileName.c_str());
	opfd=open(fileName, O_RDWR | O_CREAT | O_EXCL, 0666);
	n=read(sockfd, aBuffer.buff, sizeof(aBuffer.buff));
	while(n > 0){
	 	write(opfd,aBuffer.buff,n);
	  	n=read(sockfd, aBuffer.buff, sizeof(aBuffer.buff));
	}
	close(opfd);
	return n;
}

LServer::~LServer() {
	// TODO Auto-generated destructor stub
}
